package com.rengwuxian.coursecoroutines._2_structured_concurrency

import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.EmptyCoroutineContext
import kotlin.system.exitProcess

/**
 * 异常的结构化管理的本质：
 * CoroutineExceptionHandler、UncaughtExceptionHandler、协程的结构化异常管理
 *
 * 异常的管理的本质是什么？如何去看待和使用它？
 * 为什么协程只有在外层的父协程注册CoroutineExceptionHandler才有效果，为什么这么设计呢？
 *
 * 先说Java中线程中的UncaughtExceptionHandler：
 * UncaughtExceptionHandler： 是抓某一个特定线程的异常的
 *
 * 代码：
 * thread {
 *     throw RuntimeException("Thread error!")
 * }.setUncaughtExceptionHandler {t, e -> }
 * 这种写法有风险，线程直接启动之后，再设置，可能异常已经抛出了，还没有来的及挂上钩。
 * 可以直观的写：
 * val thread = Thread {
 *     try {
 *
 *     } catch (e: NullPointerException) {
 *
 *     }
 *     throw RuntimeException("Thread error!")
 *   }
 *   thread.setUncaughtExceptionHandler { t, e ->
 *     println("Caught $e")
 *   }
 *   thread.start()
 * 这样就一定能保证时序了。
 *
 * thread.setUncaughtExceptionHandler { t, e -> } 给某一个线程设置捕获异常的方法
 * Thread.setDefaultUncaughtExceptionHandler { t, e -> } 给整个应用所有线程设置捕获异常的方法
 * 如果两个都设置，异常会先到跟线程相关的异常捕获中去，如果异常没有捕获到，才最后由setDefaultUncaughtExceptionHandler兜底。
 *
 * 把异常吞掉，就等于没有异常？？？ 可能会增大调试难度。代码的缺陷难以发现，会出现混乱的程序运行状态（不可预判，相当严重！！！，还不如崩溃）。
 * 能预料到的异常，就多用try catch将异常解决掉，在线程内部处理。不会抛入DefaultUncaughtExceptionHandler中
 * 如果代码已经到UncaughtExceptionHandler，从线程中已经出来了（线程抛出异常，线程已经结束了），就没有机会再修复该问题了（解决异常，只能在线程内部）。（善后工作--》通用的解决方案：Log or 优雅的结束 or 再拉起）
 * 自杀：exitProcess(1)，记录Log，分析原因，完善对应代码块。
 * 实际开发中很少对某个线程设置单独的UncaughtExceptionHandler，没有意义（视你的情况而定，也可以单独重启某个功能一样的线程）。
 * 一般设置通用的就行，做的工作是一样的（收尾、自杀）。
 *
 * 这样就可以回答上面的问题，为何协程结构化只有最外层协程设置异常捕获才有用。-- 异常无法预测，只能做收尾工作，做不了具体业务上的修复。
 * 所有预料到的异常，跟平常线程写法一样，在对应的代码写try/catch，做正常修复工作即可。
 *
 *
 * 协程中：
 * CoroutineExceptionHandler： 是针对某一个协程（树）的，做不到所有协程的捕获
 * 协程是有结构化特性的，有一个协程树（某个功能实现的所有流程都在一起）遇到异常，会取消整个协程树，并在异常处理中，重启这个协程树即可。
 * 协程世界的异常，如果没有拦截，会透到线程世界。可以在线程中拦截。
 * 协程的异常处理，比线程更实用。
 *
 * 结构化异常管理：
 * 整个协程树是一体的，如果一个协程抛了异常，整个协程树就没有意义，故将异常统一设置最外层的协程处理，最合理。
 *
 * 协程和线程的异常管理，跟正常代码的try/catch一点关系都没有，不要搞混，不要觉得难学。
 */
fun main() = runBlocking<Unit> {
  Thread.setDefaultUncaughtExceptionHandler { t, e ->
    println("Caught default: $e")
    exitProcess(1)
  }
  /*val thread = Thread {
    try {

    } catch (e: NullPointerException) {

    }
    throw RuntimeException("Thread error!")
  }
  thread.setUncaughtExceptionHandler { t, e ->
    println("Caught $e")
  }
  thread.start()*/
  val scope = CoroutineScope(EmptyCoroutineContext)
  val handler = CoroutineExceptionHandler { _, exception ->
    println("Caught in Coroutine: $exception")
  }
  scope.launch(handler) {
    launch {
      throw RuntimeException("Error!")
    }
    launch {

    }
  }
  delay(10000)
}